package com.lanp.springredis.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.log.Log;
import com.lanp.springredis.model.GeoUserDto;
import com.lanp.springredis.model.UserDto;
import com.lanp.springredis.scv.RedisCSV;
import org.redisson.api.*;
import org.redisson.codec.JsonJacksonCodec;
import org.springframework.data.redis.core.RedisCommand;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author JiangPeng
 * Created with IntelliJ IDEA.
 * Description:  redisson
 * Date: 2023-08-08 09:45:53
 */
@RestController
@RequestMapping("/test/son")
public class TestSonRest {

    @Resource
    private RedissonClient redisson;
    /* --------- 对象桶的增删改查 --------- */

    @PostMapping("/bucket/save")
    public Long add(@RequestBody UserDto userDto) {
        RBucket<UserDto> bucket = redisson.getBucket(RedisCSV.user.getKey(userDto.getId()));
        bucket.set(userDto);
        return userDto.getId();
    }

    @GetMapping("/bucket/info")
    public UserDto get(UserDto userDto) {
        RBucket<UserDto> bucket = redisson.getBucket(RedisCSV.user.getKey(userDto.getId()));
        return bucket.get();
    }

    @PostMapping("/bucket/del")
    public boolean del(@RequestBody UserDto userDto) {
        RBucket<UserDto> bucket = redisson.getBucket(RedisCSV.user.getKey(userDto.getId()));
        return bucket.delete();
    }
    /* --------- 获取key列表 --------- */

    @GetMapping("/key/1")
    public Set key1() {
        RKeys keys = redisson.getKeys();
        Iterable<String> keysi = keys.getKeys();
        Set<String> set = new HashSet<>();
        keysi.forEach(k -> set.add(k));
        return set;
    }

    @GetMapping("/key/2")
    public Set key2(int page) {
        RKeys keys = redisson.getKeys();
        Iterable<String> keysi = keys.getKeysByPattern("rmapuser*");
        Set<String> set = new HashSet<>();
        keysi.forEach(k -> set.add(k));
        return set;
    }
    /* --------- map的增删改查 --------- */

    @PostMapping("/map/save")
    public Long mapAdd(@RequestBody UserDto userDto) {
        RMap<String, UserDto> map = redisson.getMap(RedisCSV.user.name());
        map.put(RedisCSV.user.getKey(userDto.getId()), userDto);
        return userDto.getId();
    }

    @PostMapping("/map/save2")
    public Long mapAdd2(@RequestBody UserDto userDto) {
        RMap<String, Object> map = redisson.getMap("rmap" + RedisCSV.user.getKey(userDto.getId()));
        Map<String, Object> userDtoMap = BeanUtil.beanToMap(userDto, false, true);
        map.putAll(userDtoMap);
        return userDto.getId();
    }

    @GetMapping("/map/info")
    public UserDto mapGet(UserDto userDto) {
        RMap<String, UserDto> map = redisson.getMap(RedisCSV.user.name());
        UserDto sonUserDto = map.get(RedisCSV.user.getKey(userDto.getId()));
        return sonUserDto;
    }

    @GetMapping("/map/info2")
    public UserDto mapGet2(UserDto userDto) {
        RMap<String, Object> map = redisson.getMap("rmap" + RedisCSV.user.getKey(userDto.getId()));
        UserDto sonUserDto = BeanUtil.mapToBean(map, UserDto.class, true);
        return sonUserDto;
    }

    @PostMapping("/map/del")
    public UserDto mapDel(@RequestBody UserDto userDto) {
        RMap<String, UserDto> map = redisson.getMap(RedisCSV.user.name());
        return map.remove(RedisCSV.user.getKey(userDto.getId()));
    }

    /* --------- topic消息订阅 --------- */
    @PostMapping("/topic/producer")
    public String producer(String msg) {
        //生产消息
        RTopic topic = redisson.getTopic(RedisCSV.topic_user.getKey());
        topic.publish(msg);
        return "发送->" + msg + " 成功";
    }


    /* --------- bloom --------- */
    @PostMapping("/bloom/exit")
    public String bloom(Integer id) {
        //生产消息
        RBloomFilter<Integer> bloomFilter = redisson.getBloomFilter(RedisCSV.bloom_user.getKey());
        bloomFilter.tryInit(1000000l, 0.01);
        if (bloomFilter.contains(id)) {
            return id + "已存在";
        } else {
            bloomFilter.add(id);
            return id + "不存在";
        }
    }


    /* --------- rateLimiter分布式限流器 --------- */
    @GetMapping("/limit/a")
    public String limitA() throws InterruptedException {
        //RRateLimiter rateLimiter = redisson.getRateLimiter(RedisCSV.limit_user.getKey());
        //rateLimiter.trySetRate(RateType.OVERALL, 3, 5, RateIntervalUnit.SECONDS);
        //boolean b = rateLimiter.tryAcquire(1);
        //if (b) {
        //    return "请求成功";
        //} else {
        //    return "请求过于频繁";
        //}
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss");
        System.out.println("请求时间" + sdf.format(new Date()));
        // 1、 声明一个限流器
        RRateLimiter rateLimiter = redisson.getRateLimiter("rete_limiter667665645566");
        // 2、 设置速率，10秒中产生3个令牌
        rateLimiter.trySetRate(RateType.OVERALL, 3, 5, RateIntervalUnit.SECONDS);

        // 3、试图获取一个令牌，获取到返回true
        //for (int i = 0; i < 10; i++) {
        //    boolean b = rateLimiter.tryAcquire(1);
        //    System.out.println(b);
        //    Thread.sleep(500);
        //}

        boolean b = rateLimiter.tryAcquire(1);
        Thread.sleep(1000);
        System.out.println(b);
        if (b) {
            return "请求成功";
        } else {
            throw new RuntimeException("请求过于频繁");
        }
    }

    /* --------- localCachedMap 本地缓存map --------- */
    private LocalCachedMapOptions options = LocalCachedMapOptions.defaults()
            // 用于淘汰清除本地缓存内的元素
            // 共有以下几种选择:
            // LFU - 统计元素的使用频率，淘汰用得最少(最不常用)的
            // LRU - 按元素使用时间排序比较，淘汰最早(最久远)的。
            // SOFT - 元素用Java的weakReference来保存，缓存元素通过GC过程清除
            // WEAK - 元素用Java的softReference来保存，缓存元素通过GC过程清除
            // NONE - 永不淘汰清除缓在元素。
            .evictionPolicy(LocalCachedMapOptions.EvictionPolicy.NONE)
            // 如果缓存容量值为9表示不限制本地缓存容量大小
            .cacheSize(1000)
            // 以下选项适用于断线原因造成了未收到本地缓在更新消息的情况
            // 断线重连的策略有以下几种:
            // CLEAR - 如断线一段时间以后则在重新建立连接以后清空本地缓存
            // LOAD - 在服务端保在一份10分钟的作废日志
            //         如果10分钟内重新建立连接，则按照作废日志内的记录清空本地缓在的元素
            //         如果断线时间超过了这个时间，则将清空本地缓存中所有的内容
            // NONE -默认值。断线重连时不做处理
            .reconnectionStrategy(LocalCachedMapOptions.ReconnectionStrategy.NONE)
            // 以下选项适用于不同本地缓存之间相互保持同步的情况
            //缓存同步策略有以下几种:
            // NVALIDATE  默认值。当本地缓存映射的某条元素发生变动时，同时驱逐所有相同本地缓存映射内的该元素
            // UPDATE  当本地缓在映射的某条元素发生变动时，同时更新所有相同本地缓存映射内的该元素
            // NONE - 不做任何同步处理
            .syncStrategy(LocalCachedMapOptions.SyncStrategy.INVALIDATE)
            // 每个Map本地缓存里元素的有效时间，默认毫秒为单位
            .timeToLive(1000).timeToLive(10, TimeUnit.SECONDS)
            // 每个Map本地缓存里元素的最长闲置时间，默认毫秒为单位
            .maxIdle(1000).maxIdle(10, TimeUnit.SECONDS);


    @PostMapping("/lcmap/add")
    public Long lcMapAdd(@RequestBody UserDto userDto) {
        RLocalCachedMap<String, Object> localCachedMap = redisson.getLocalCachedMap("lcmap" + RedisCSV.user.getKey(userDto.getId()), options);
        Map<String, Object> userDtoMap = BeanUtil.beanToMap(userDto, false, true);
        localCachedMap.putAll(userDtoMap);
        return userDto.getId();
    }

    @GetMapping("/lcmap/info")
    public UserDto lcMapInfo(UserDto userDto) {
        RLocalCachedMap<String, Object> localCachedMap = redisson.getLocalCachedMap("lcmap" + RedisCSV.user.getKey(userDto.getId()), options);
        UserDto lcUser = BeanUtil.mapToBean(localCachedMap, UserDto.class, true);
        return lcUser;
    }

    /* --------- mapCache --------- */
    @PostMapping("/cmap/add")
    public Long cMapAdd(@RequestBody UserDto userDto) {
        RMapCache<String, Object> mapCache = redisson.getMapCache(RedisCSV.cmap_user.getKey());
        Map<String, Object> userDtoMap = BeanUtil.beanToMap(userDto, false, true);
        userDtoMap.forEach((k, v) -> {
            mapCache.put(k, v, 10, TimeUnit.SECONDS);
        });
        return userDto.getId();
    }

    @GetMapping("/cmap/info")
    public UserDto cMapInfo(UserDto userDto) {
        RMapCache<String, Object> mapCache = redisson.getMapCache(RedisCSV.cmap_user.getKey());
        UserDto lcUser = BeanUtil.mapToBean(mapCache, UserDto.class, true);
        return lcUser;
    }

    @GetMapping("/cmap/del")
    public Long cMapInfo(Long id, String key) {
        RMapCache<String, Object> mapCache = redisson.getMapCache(RedisCSV.cmap_user.getKey());
        mapCache.rename(key);
        return id;
    }

    /* --------- multiMap多值属性map --------- */

    @PostMapping("/mmap/add")
    public Long mMapAdd(@RequestBody UserDto userDto) {
        RListMultimap<String, Object> listMultimap = redisson.getListMultimap(RedisCSV.mmap_user.getKey(userDto.getId()));
        Map<String, Object> userDtoMap = BeanUtil.beanToMap(userDto, false, true);
        userDtoMap.forEach((k, v) -> listMultimap.put(k, v));
        return userDto.getId();
    }

    @GetMapping("/mmap/info")
    public Object mMapInfo(UserDto userDto) {
        RListMultimap<String, Object> listMultimap = redisson.getListMultimap(RedisCSV.mmap_user.getKey(userDto.getId()));
        Map<String, Object> res = new HashMap(listMultimap.size(), 1);
        listMultimap.keySet().forEach(k -> res.put(k, listMultimap.get(k)));
        return res;
    }

    /* --------- set --------- */
    @PostMapping("/set/add")
    public String setAdd(@RequestBody UserDto userDto) {
        RSet<Object> set = redisson.getSet(RedisCSV.set.getKey());
        set.add(userDto.getName());
        return userDto.getName();
    }

    @GetMapping("/set/info")
    public Object setInfo() {
        RSet<Object> set = redisson.getSet(RedisCSV.set.getKey());
        Set<Object> objects = set.readAll();
        return objects;
    }

    /* --------- geo --------- */
    @PostMapping("/geo/add")
    public Long geoAdd(@RequestBody GeoUserDto geoUserDto) {
        RGeo<Object> geo = redisson.getGeo(RedisCSV.geo.getKey(), JsonJacksonCodec.INSTANCE);
        geo.add(geoUserDto.getX(), geoUserDto.getY(), geoUserDto.getUserDto());
        return geoUserDto.getUserDto().getId();
    }

    @GetMapping("/geo/info")
    public Object geoInfo() {
        RGeo<Object> geo = redisson.getGeo(RedisCSV.geo.getKey(), JsonJacksonCodec.INSTANCE);
        List<Object> radius = geo.radius(116.405285, 39.904989, 100, GeoUnit.KILOMETERS);
        return radius;
    }

    /* --------- BitSet --------- */
    @PostMapping("/bitset/add")
    public Long bsAdd(@RequestBody UserDto userDto) {
        RBitSet bitSet = redisson.getBitSet(RedisCSV.bitset2.getKey());
        bitSet.set(userDto.getId(), userDto.isVal());
        return userDto.getId();
    }

    @PostMapping("/bitset/info")
    public Object bsInfo(@RequestBody UserDto userDto) {
        RBitSet bitSet = redisson.getBitSet(RedisCSV.bitset2.getKey());
        return bitSet.get(userDto.getId());
    }

    @PostMapping("/bitset/all")
    public Object bsAll(@RequestBody UserDto userDto) {
        RBitSet bitSet = redisson.getBitSet(RedisCSV.bitset2.getKey());
        return bitSet.size();
    }

    @PostMapping("/bitset/exist")
    public Object bsExits(@RequestBody UserDto userDto) {
        RBitSet bitSet = redisson.getBitSet(RedisCSV.bitset2.getKey());
        return bitSet.getLong(userDto.getId());
    }

    @PostMapping("/bitset/exist2")
    public Object bsExits2(@RequestBody UserDto userDto) {
        RBitSet bitSet = redisson.getBitSet(RedisCSV.bitset2.getKey());
        return bitSet.incrementAndGetLong(userDto.getId(), 0);
    }

    @PostMapping("/bitset/count")
    public Object bsCount(@RequestBody UserDto userDto) {
        RBitSet bitSet = redisson.getBitSet(RedisCSV.bitset2.getKey());
        return bitSet.cardinality();
    }

    /* --------- atomicLong --------- */
    @PostMapping("/atomic/add1")
    public Object atomicAdd1(@RequestBody UserDto userDto) {
        RAtomicLong atomicLong = redisson.getAtomicLong(RedisCSV.atomic.getKey());
        return atomicLong.incrementAndGet();
    }

    @PostMapping("/atomic/add")
    public Object atomicAdd(@RequestBody UserDto userDto) {
        RAtomicLong atomicLong = redisson.getAtomicLong(RedisCSV.atomic.getKey());
        return atomicLong.addAndGet(userDto.getId());
    }

    @PostMapping("/atomic/sub1")
    public Object atomicSub1(@RequestBody UserDto userDto) {
        RAtomicLong atomicLong = redisson.getAtomicLong(RedisCSV.atomic.getKey());
        return atomicLong.decrementAndGet();
    }

    /* --------- hyperLogLog --------- */
    @PostMapping("/hll/add")
    public Object hllAdd(@RequestBody UserDto userDto) {
        RHyperLogLog<Object> hyperLogLog = redisson.getHyperLogLog("hyperLogLog", JsonJacksonCodec.INSTANCE);
        hyperLogLog.add("1");
        hyperLogLog.add("2");
        hyperLogLog.add("3");
        hyperLogLog.add(1);
        hyperLogLog.add(2);
        return hyperLogLog.count();
    }

    @PostMapping("/hll/info")
    public Object hllInfo(@RequestBody UserDto userDto) {
        RHyperLogLog<Object> hyperLogLog = redisson.getHyperLogLog("hyperLogLog", JsonJacksonCodec.INSTANCE);
        return hyperLogLog.count();
    }

    /* --------- LongAdder --------- */
    @PostMapping("/long/add")
    public Object LongAdder(@RequestBody UserDto userDto) {
        RLongAdder longAdder = redisson.getLongAdder("LongAdder");
        longAdder.add(1);
        longAdder.add(5);
        longAdder.increment();
        longAdder.decrement();
        longAdder.increment();
        return longAdder.sum();
    }

    /* --------- scoredSortedSet 计分排序 --------- */
    @PostMapping("/sss/add")
    public Object sssAdd(@RequestBody UserDto userDto) {
        RScoredSortedSet<UserDto> scoredSortedSet = redisson.getScoredSortedSet("scoredSortedSet");
        return scoredSortedSet.add(userDto.getScore(), userDto);
    }

    @PostMapping("/sss/first")
    public Object sssFirst(@RequestBody UserDto userDto) {
        RScoredSortedSet<UserDto> scoredSortedSet = redisson.getScoredSortedSet("scoredSortedSet");
        Map<String, Object> res = new HashMap<>();
        res.put("first", scoredSortedSet.first());
        res.put("last", scoredSortedSet.last());
        res.put("rank", scoredSortedSet.rank(userDto));
        res.put("score", scoredSortedSet.getScore(userDto));
        res.put("list", scoredSortedSet.stream().limit(10));
        res.put("page", scoredSortedSet.valueRange(2, 3));
        res.put("scorePage", scoredSortedSet.valueRange(0.33123, false, 0.52, false));
        return res;
    }


    /* --------- lock --------- */
    @GetMapping("/lock/test1")
    public Object lockTest1() {
        RLock lock1 = redisson.getLock("lock1");
        boolean isLock = false;
        try {
            isLock = lock1.tryLock(500, 1000, TimeUnit.MILLISECONDS);
            if (isLock) {
                Thread.sleep(2000);
                return "success";
            }
        } catch (InterruptedException e) {
            System.out.println("获取锁异常");
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println("锁异常");
            e.printStackTrace();
        } finally {
            if (lock1 != null) {
                try {
                    lock1.unlock();
                } catch (Exception e) {
                    System.out.println("锁解锁异常");
                    e.printStackTrace();
                }
            }
        }
        return "error";
    }


}
